package cytargetlinker.conversion;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.bridgedb.DataSource;
import org.bridgedb.IDMapper;
import org.bridgedb.IDMapperStack;
import org.bridgedb.Xref;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.pathvisio.core.model.Pathway;

import cytargetlinker.conversion.ArgsParser.AFilesAttributes;
import cytargetlinker.conversion.ArgsParser.AFilesIn;
import cytargetlinker.conversion.ArgsParser.AFilesOut;
import cytargetlinker.conversion.ArgsParser.AHelp;
import cytargetlinker.conversion.ArgsParser.GraphBuilder;
import cytargetlinker.conversion.graph.Graph;
import cytargetlinker.conversion.graph.Graph.Node;

public class WP2XgmmlConverter {

	private final static Logger log = Logger.getLogger(WP2XgmmlConverter.class.getName());
	private static Args pargs;
	private interface Args extends AHelp, AFilesIn, AFilesOut, AFilesAttributes {}
	
	/**
	 * MAIN METHOD
	 * USAGE: java -cp conversion.jar cytargetlinker.conversion.Microcosm 
	 * ARGUMENTS: 
	 * -i = input file
	 * -o = output file
	 * --bridgeDbTarget = BridgeDb mapping file for the target nodes (gene database)
	 * --organism "mouse" or "human" 
	 * @throws Exception 
	 */
	public static void main(String argv[]) throws Exception {
		pargs = ArgsParser.parse(argv, Args.class);
		if(pargs.getOrganism() != null) {
			WP2XgmmlConverter converter = new WP2XgmmlConverter();
			converter.startConversion();
		} else {
			log.severe("Please specify organism (--organism Homo sapiens or Mus musculus).");
		}
	}
	
	public WP2XgmmlConverter() {
		edges = new HashMap<String, List<String>>();
		foundConnections = new ArrayList<String>();
	}
	
	private IDMapperStack gdb;
	private Graph graph;
	private Map<String, List<String>> edges;
	
	private List<String> foundConnections;
	
	private int countEdge = 0;
	private boolean mapping = true;
	
	private void startConversion() throws Exception {
		File in = pargs.getInput().get(0);
		if(in != null) {
			Utils.setUpLogger(log, in, false);
			gdb = Utils.initIDMapper(null, pargs.getBridgeDbFileTarget());
			if(gdb == null) {
				mapping = false;
				log.info("no identifier mapping");
			}
			log.info("Conversion of WikiPathways collection started\n");

			ArgsParser.convertAndWrite(pargs, pargs, new GraphBuilder() {
				public Graph buildGraph(File in) throws Exception {
					return parseWikiPathways(in);
				}
			});
				
			log.info("WikiPathways association network file created.\n\n");
		} else {
			System.out.println("Please specify input file!");
		}
	}
	
	private String getSysCodeIn() {
		if(pargs.getOrganism().equals("Homo sapiens")) {
			return "EnHs";
		} else if (pargs.getOrganism().equals("Mus musculus")) {
			return "EnMm";
		}
		return "";
	}
	
	private Graph parseWikiPathways(File in) {
		graph = new Graph();
		
		setNetworkAttributes(in);
		parsePathwayCollection(in);
		
		log.info(foundConnections.size() + " interactions have been found.\n");
		return graph;
	}

	private void parsePathwayCollection(File in) {
		if(in.isDirectory()) {
			for (File file : in.listFiles()) {
				try {
					List<String> list = new ArrayList<String>();
					Pathway pathway = new Pathway();
					pathway.setSourceFile(file);
					pathway.readFromXml(file, false);
					String title = getTitle(file);
					log.info("Start conversion of pathway " + title + "\n");
					createPathwayNode(pathway.getSourceFile().getName(), title);
					int count = 0;
					for(Xref xref : pathway.getDataNodeXrefs()) {
						System.out.println(xref.getId() + "\t" + xref.getDataSource());
						if(xref != null &&  xref.getId() != null && xref.getDataSource() != null && !xref.getId().equals("") && !xref.getDataSource().equals("")) {
							Set<Xref> result = gdb.mapID(xref, DataSource.getBySystemCode(getSysCodeIn()));
							if(result.size() > 0) {
								for(Xref x : result) {
									if(!list.contains(x.getId())) {
										createGeneNode(x.getId(), gdb);
										count++;
										addEdge(x.getId(), pathway.getSourceFile().getName());
										list.add(x.getId());
									}
								}
							}
						}
					}
					log.info("Pathway converter with " + count + " genes.\n");
				} catch (Exception e) {
					log.info("Couldn't convert " + file.getAbsolutePath() + "\n");
				}
			} 
		}
	}
	
	private String getTitle(File file) throws JDOMException, IOException {
		SAXBuilder builder = new SAXBuilder();
		Document document = (Document) builder.build(file);
		Element rootNode = document.getRootElement();
		Attribute str = rootNode.getAttribute("Name");
		System.out.println(rootNode);
		return str.getValue();
	}
	
	private void addEdge(String gene, String pathway) {
		if(edges.containsKey(gene)) {
			if(!edges.get(gene).contains(pathway)) {
				graph.addEdge("" + countEdge, graph.getNode(pathway),graph.getNode(gene));
				edges.get(gene).add(pathway);
				foundConnections.add(gene + "\t" + pathway);
				countEdge++;
			}
		} else {
			graph.addEdge("" + countEdge, graph.getNode(pathway),graph.getNode(gene));
			List<String> list = new ArrayList<String>();
			list.add(pathway);
			foundConnections.add(gene + "\t" + pathway);
			edges.put(gene, list);
			countEdge++;
		}
	}
	
	private void createGeneNode(String geneId, IDMapper mapper) {
		
		Map<String, String> attr = new HashMap<String, String>();
		attr.put("label", geneId);
		attr.put("name", geneId);
		attr.put("organism", pargs.getOrganism());
		attr.put("Ensembl", geneId);
		
		if(!mapping) {
			Utils.createNode(graph, geneId, attr);
		} else {
			List<String> sysCodeOut = new ArrayList<String>();
			sysCodeOut.add(getSysCodeIn());
			sysCodeOut.add("H");
			sysCodeOut.add("L");
			
			Utils.createNode(graph, geneId, getSysCodeIn(), sysCodeOut, attr, mapper);
		}
	}
	
	private Node createPathwayNode(String id, String name) {
		Node node = graph.addNode(id);
		node.appendAttribute("identifiers", "[" + id + "]");
		node.appendAttribute("label", name);
		node.appendAttribute("name", name);
		return node;
	}

	private void setNetworkAttributes(File in) {
		graph.setTitle("WikiPathways Homo sapiens");
		graph.setAttribute(CommonAttributes.DATABASE.getName(), "WikiPathways Homo sapiens");
		graph.setAttribute(CommonAttributes.TYPE.getName(), "pathway annotations");
		graph.setAttribute(CommonAttributes.SOURCE_FILE.getName(), "WikiPathways analysis collection 2012-07-2012");
		graph.setAttribute(CommonAttributes.IDENTIFIERS.getName(), "identifiers");
		graph.setAttribute(CommonAttributes.TARGET_DATASOURCE.getName(), "Wikipathways");
		graph.setAttribute(CommonAttributes.SOURCE_DATASOURCE.getName(), "Ensembl");
		graph.setAttribute(CommonAttributes.SOURCE_TYPE.getName(), "pathway");
		graph.setAttribute(CommonAttributes.TARGET_TYPE.getName(), "gene");
		graph.setAttribute("Download-Date", "2012-07-23");
	}
}
